<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.5: http://docutils.sourceforge.net/" />
<title>Tutorial</title>
<style type="text/css">

/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 5196 2007-06-03 20:25:28Z wiemann $
:Copyright: This stylesheet has been placed in the public domain.

Default cascading style sheet for the HTML output of Docutils.

See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/

/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
  border: 0 }

table.borderless td, table.borderless th {
  /* Override padding for "table.docutils td" with "! important".
     The right padding separates the table cells. */
  padding: 0 0.5em 0 0 ! important }

.first {
  /* Override more specific margin styles with "! important". */
  margin-top: 0 ! important }

.last, .with-subtitle {
  margin-bottom: 0 ! important }

.hidden {
  display: none }

a.toc-backref {
  text-decoration: none ;
  color: black }

blockquote.epigraph {
  margin: 2em 5em ; }

dl.docutils dd {
  margin-bottom: 0.5em }

/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
  font-weight: bold }
*/

div.abstract {
  margin: 2em 5em }

div.abstract p.topic-title {
  font-weight: bold ;
  text-align: center }

div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
  margin: 2em ;
  border: medium outset ;
  padding: 1em }

div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
  font-weight: bold ;
  font-family: sans-serif }

div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title {
  color: red ;
  font-weight: bold ;
  font-family: sans-serif }

/* Uncomment (and remove this text!) to get reduced vertical space in
   compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
  margin-bottom: 0.5em }

div.compound .compound-last, div.compound .compound-middle {
  margin-top: 0.5em }
*/

div.dedication {
  margin: 2em 5em ;
  text-align: center ;
  font-style: italic }

div.dedication p.topic-title {
  font-weight: bold ;
  font-style: normal }

div.figure {
  margin-left: 2em ;
  margin-right: 2em }

div.footer, div.header {
  clear: both;
  font-size: smaller }

div.line-block {
  display: block ;
  margin-top: 1em ;
  margin-bottom: 1em }

div.line-block div.line-block {
  margin-top: 0 ;
  margin-bottom: 0 ;
  margin-left: 1.5em }

div.sidebar {
  margin: 0 0 0.5em 1em ;
  border: medium outset ;
  padding: 1em ;
  background-color: #ffffee ;
  width: 40% ;
  float: right ;
  clear: right }

div.sidebar p.rubric {
  font-family: sans-serif ;
  font-size: medium }

div.system-messages {
  margin: 5em }

div.system-messages h1 {
  color: red }

div.system-message {
  border: medium outset ;
  padding: 1em }

div.system-message p.system-message-title {
  color: red ;
  font-weight: bold }

div.topic {
  margin: 2em }

h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
  margin-top: 0.4em }

h1.title {
  text-align: center }

h2.subtitle {
  text-align: center }

hr.docutils {
  width: 75% }

img.align-left {
  clear: left }

img.align-right {
  clear: right }

ol.simple, ul.simple {
  margin-bottom: 1em }

ol.arabic {
  list-style: decimal }

ol.loweralpha {
  list-style: lower-alpha }

ol.upperalpha {
  list-style: upper-alpha }

ol.lowerroman {
  list-style: lower-roman }

ol.upperroman {
  list-style: upper-roman }

p.attribution {
  text-align: right ;
  margin-left: 50% }

p.caption {
  font-style: italic }

p.credits {
  font-style: italic ;
  font-size: smaller }

p.label {
  white-space: nowrap }

p.rubric {
  font-weight: bold ;
  font-size: larger ;
  color: maroon ;
  text-align: center }

p.sidebar-title {
  font-family: sans-serif ;
  font-weight: bold ;
  font-size: larger }

p.sidebar-subtitle {
  font-family: sans-serif ;
  font-weight: bold }

p.topic-title {
  font-weight: bold }

pre.address {
  margin-bottom: 0 ;
  margin-top: 0 ;
  font-family: serif ;
  font-size: 100% }

pre.literal-block, pre.doctest-block {
  margin-left: 2em ;
  margin-right: 2em }

span.classifier {
  font-family: sans-serif ;
  font-style: oblique }

span.classifier-delimiter {
  font-family: sans-serif ;
  font-weight: bold }

span.interpreted {
  font-family: sans-serif }

span.option {
  white-space: nowrap }

span.pre {
  white-space: pre }

span.problematic {
  color: red }

span.section-subtitle {
  /* font-size relative to parent (h1..h6 element) */
  font-size: 80% }

table.citation {
  border-left: solid 1px gray;
  margin-left: 1px }

table.docinfo {
  margin: 2em 4em }

table.docutils {
  margin-top: 0.5em ;
  margin-bottom: 0.5em }

table.footnote {
  border-left: solid 1px black;
  margin-left: 1px }

table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
  padding-left: 0.5em ;
  padding-right: 0.5em ;
  vertical-align: top }

table.docutils th.field-name, table.docinfo th.docinfo-name {
  font-weight: bold ;
  text-align: left ;
  white-space: nowrap ;
  padding-left: 0 }

h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
  font-size: 100% }

ul.auto-toc {
  list-style-type: none }

</style>
</head>
<body>
<div class="document" id="tutorial">
<h1 class="title">Tutorial</h1>

<p>This tutorial is a work-in-progress.</p>
<div class="contents topic" id="contents">
<p class="topic-title first">Contents</p>
<ul class="simple">
<li><a class="reference internal" href="#part-i-forms-widgets-and-applications" id="id1">Part I: Forms, Widgets and Applications</a><ul>
<li><a class="reference internal" href="#using-a-wrapper" id="id2">Using a wrapper</a></li>
<li><a class="reference internal" href="#using-a-form" id="id3">Using a Form</a></li>
<li><a class="reference internal" href="#adding-the-first-widget" id="id4">Adding the first widget</a></li>
<li><a class="reference internal" href="#let-s-be-a-little-more-object-oriented" id="id5">Let's be a little more object-oriented</a></li>
<li><a class="reference internal" href="#offering-choice" id="id6">Offering Choice</a></li>
<li><a class="reference internal" href="#being-even-more-object-oriented" id="id7">Being Even More Object-Oriented</a></li>
<li><a class="reference internal" href="#choosing-an-approach" id="id8">Choosing an approach</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="part-i-forms-widgets-and-applications">
<h1><a class="toc-backref" href="#id1">Part I: Forms, Widgets and Applications</a></h1>
<div class="section" id="using-a-wrapper">
<h2><a class="toc-backref" href="#id2">Using a wrapper</a></h2>
<p>Switiching into and out of a curses environment is a very boring task.  The python curses module provides a wrapper to do this, and this is exposed by npyscreen as wrapper_basic.  The basic framework for a very simple application looks like this:</p>
<pre class="literal-block">
import npyscreen

def myFunction(*args):
    pass

if __name__ == '__main__':
    npyscreen.wrapper_basic(myFunction)
    print &quot;Blink and you missed it!&quot;
</pre>
<p>Which doesn't do anything clever.  The curses environment starts and exits without actually doing anything.  But it's a start.</p>
<p>Note that npyscreen also provides other wrappers that do slightly different things.</p>
</div>
<div class="section" id="using-a-form">
<h2><a class="toc-backref" href="#id3">Using a Form</a></h2>
<p>Now let's try putting something on the screen.  For that we need a <em>Form</em> instance:</p>
<pre class="literal-block">
F = npyscreen.Form(name='My Test Application')
</pre>
<p>should do the trick.  Let's put that into our wrapper:</p>
<pre class="literal-block">
import npyscreen

def myFunction(*args):
    F = npyscreen.Form(name='My Test Application')

if __name__ == '__main__':
    npyscreen.wrapper_basic(myFunction)
    print &quot;Blink and you missed it!&quot;
</pre>
<p>Which still seems to do nothing -- because we haven't actually displayed the Form.  <em>F.display()</em> would put it on the screen, but we actually want to let the user play with it, so let's do F.edit() instead:</p>
<pre class="literal-block">
import npyscreen

def myFunction(*args):
    F = npyscreen.Form(name='My Test Application')
    F.edit()

if __name__ == '__main__':
    npyscreen.wrapper_basic(myFunction)
    print &quot;Blink and you missed it!&quot;
</pre>
<p>Which won't run, because when we try to edit the Form npyscreen discovers there's no widget to edit.  Let's put that right.</p>
</div>
<div class="section" id="adding-the-first-widget">
<h2><a class="toc-backref" href="#id4">Adding the first widget</a></h2>
<p>Let's put a textbox with a title in place.  We do that with the code:</p>
<pre class="literal-block">
F.add(npyscreen.TitleText, name=&quot;First Widget&quot;)
</pre>
<p>The full code is:</p>
<pre class="literal-block">
import npyscreen

def myFunction(*args):
    F = npyscreen.Form(name='My Test Application')
    F.add(npyscreen.TitleText, name=&quot;First Widget&quot;)
    F.edit()

if __name__ == '__main__':
    npyscreen.wrapper_basic(myFunction)
    print &quot;Blink and you missed it!&quot;
</pre>
<p>Much better! That gives us something looking like an application.  With just a three small changes we can change closing the message displayed to whatever the user typed:</p>
<pre class="literal-block">
import npyscreen

def myFunction(*args):
    F = npyscreen.Form(name='My Test Application')
    myFW = F.add(npyscreen.TitleText, name=&quot;First Widget&quot;)   # &lt;------- Change 1
    F.edit()
    return myFW.value   # &lt;------- Change 2

if __name__ == '__main__':
    print npyscreen.wrapper_basic(myFunction)  # &lt;---- and change 3
</pre>
</div>
<div class="section" id="let-s-be-a-little-more-object-oriented">
<h2><a class="toc-backref" href="#id5">Let's be a little more object-oriented</a></h2>
<p>The approach we've been using works fine for simple applications, but once we start creating lots of widgets on a form, it is better to tuck all of that code away inside a Form object.</p>
<p>Instead of using the base Form() class in a very procedural way, let's create our own Form class.  We'll override the Form's <em>create()</em> method, which is called whenever a Form is created:</p>
<pre class="literal-block">
class myEmployeeForm(npyscreen.Form):
    def create(self):
        super(myEmployeeForm, self).create()  # This line is not strictly necessary: the API promises that the create method does nothing by default.
                                              # I've ommitted it from later example code.
        self.myName        = self.add(npyscreen.TitleText, name='Name')
        self.myDepartment  = self.add(npyscreen.TitleText, name='Department')
        self.myDate        = self.add(npyscreen.TitleDateCombo, name='Date Employed')
</pre>
<p>We can use our wrapper code from before to use it:</p>
<pre class="literal-block">
import npyscreen

class myEmployeeForm(npyscreen.Form):
    def create(self):
        self.myName        = self.add(npyscreen.TitleText, name='Name')
        self.myDepartment  = self.add(npyscreen.TitleText, name='Department')
        self.myDate        = self.add(npyscreen.TitleDateCombo, name='Date Employed')

def myFunction(*args):
    F = myEmployeeForm(name = &quot;New Employee&quot;)
    F.edit()
    return &quot;Created record for &quot; + F.myName.value

if __name__ == '__main__':
    print npyscreen.wrapper_basic(myFunction)
</pre>
</div>
<div class="section" id="offering-choice">
<h2><a class="toc-backref" href="#id6">Offering Choice</a></h2>
<p>Actually, we probably don't want just any old department name typed in - we want to offer a list of choices.  Let's use the TitleSelectOne widget.  It's a multi-line widget, so we need to take care that it takes up only a few lines of the screen (left to itself it would take up all the remaining space on the screen):</p>
<pre class="literal-block">
self.myDepartment = self.add(npyscreen.TitleSelectOne, max_height=3,
                                name='Department',
                                values = ['Department 1', 'Department 2', 'Department 3'],
                                scroll_exit = True  # Let the user move out of the widget by pressing the down arrow instead of tab.  Try it without
                                                    # to see the difference.
                                )
</pre>
<p>Putting that in context:</p>
<pre class="literal-block">
import npyscreen

class myEmployeeForm(npyscreen.Form):
    def create(self):
        self.myName        = self.add(npyscreen.TitleText, name='Name')
        self.myDepartment = self.add(npyscreen.TitleSelectOne, scroll_exit=True, max_height=3, name='Department', values = ['Department 1', 'Department 2', 'Department 3'])
        self.myDate        = self.add(npyscreen.TitleDateCombo, name='Date Employed')

def myFunction(*args):
    F = myEmployeeForm(name = &quot;New Employee&quot;)
    F.edit()
    return &quot;Created record for &quot; + F.myName.value

if __name__ == '__main__':
    print npyscreen.wrapper_basic(myFunction)
</pre>
</div>
<div class="section" id="being-even-more-object-oriented">
<h2><a class="toc-backref" href="#id7">Being Even More Object-Oriented</a></h2>
<p>What we've done so far is all very well, but still ugly at the edges.  We're still calling F.edit() ourselves, which is fine in a single-form application, but could lead to problems with recursion-depth later if we are not careful.  Fortunately, npyscreen can manage the display and editing of Forms.  We need to use the <em>NPSAppManaged</em> class.</p>
<p>Let's scrap the framework that has supported us so far, and start with a different basis for our application:</p>
<pre class="literal-block">
import npyscreen

class MyApplication(npyscreen.NPSAppManaged):
    pass

 if __name__ == '__main__':
    TestApp = MyApplication().run()
    print &quot;All objects, baby.&quot;
</pre>
<p>Which will exit with an exception, because you have no 'MAIN' Form, which is the starting point for all NPSAppManaged applications.</p>
<p>Let's put that right.  We'll use the Form class from before:</p>
<pre class="literal-block">
 import npyscreen

 class myEmployeeForm(npyscreen.Form):
     def create(self):
        self.myName        = self.add(npyscreen.TitleText, name='Name')
        self.myDepartment = self.add(npyscreen.TitleSelectOne, scroll_exit=True, max_height=3, name='Department', values = ['Department 1', 'Department 2', 'Department 3'])
        self.myDate        = self.add(npyscreen.TitleDateCombo, name='Date Employed')

class MyApplication(npyscreen.NPSAppManaged):
    def onStart(self):
        self.addForm('MAIN', myEmployeeForm, name='New Employee')

if __name__ == '__main__':
    TestApp = MyApplication().run()
    print &quot;All objects, baby.&quot;
</pre>
<p>If you run the above code, you'll find yourself frustrated, because the application will continually display the form for you to edit, and you'll have to press &quot;^C&quot; (Control C) to exit.</p>
<p>That's because the NPSAppManaged class continually displays whatever form is named by its NEXT_ACTIVE_FORM attribute (in this case, the default - 'MAIN').</p>
<p>Let's alter the myEmployeeForm to tell it that after being run in an NPSAppManaged context, it should tell its NPSAppManaged parent to stop displaying Forms.  We do that by creating the special method called <em>afterEditing</em>:</p>
<pre class="literal-block">
class myEmployeeForm(npyscreen.Form):
    def afterEditing(self):
        self.parentApp.NEXT_ACTIVE_FORM = None

    def create(self):
       self.myName        = self.add(npyscreen.TitleText, name='Name')
       self.myDepartment = self.add(npyscreen.TitleSelectOne, scroll_exit=True, max_height=3, name='Department', values = ['Department 1', 'Department 2', 'Department 3'])
       self.myDate        = self.add(npyscreen.TitleDateCombo, name='Date Employed')
</pre>
<p>If we preferred, we could achieve the same result by defining a special method <em>onInMainLoop</em> in our MyApplication class - this method would get called after each form has been edited.</p>
<p>Our code now looks like this:</p>
<pre class="literal-block">
import npyscreen

class myEmployeeForm(npyscreen.Form):
    def afterEditing(self):
        self.parentApp.NEXT_ACTIVE_FORM = None

    def create(self):
       self.myName        = self.add(npyscreen.TitleText, name='Name')
       self.myDepartment = self.add(npyscreen.TitleSelectOne, scroll_exit=True, max_height=3, name='Department', values = ['Department 1', 'Department 2', 'Department 3'])
       self.myDate        = self.add(npyscreen.TitleDateCombo, name='Date Employed')

class MyApplication(npyscreen.NPSAppManaged):
   def onStart(self):
       self.addForm('MAIN', myEmployeeForm, name='New Employee')
       # A real application might define more forms here.......

if __name__ == '__main__':
   TestApp = MyApplication().run()
</pre>
</div>
<div class="section" id="choosing-an-approach">
<h2><a class="toc-backref" href="#id8">Choosing an approach</a></h2>
<p>The last example above is probably over-kill for a very simple application.  But it provides a much more robust framework with which to build larger applications than the framework we used at the start of the tutorial, at the cost of only a few lines of code.  If you are displaying more than one screen, or running an application continuously, this is probably the approach you should take.</p>
</div>
</div>
</div>
</body>
</html>
